/**
 *  @(#)  IFAAAuthenticator  module    1.0  2017/03/24
 *
 *  Copyright  (c)  2017,  IFAA  and/or  its  affiliates.  All  rights  reserved.
 *
 *  Redistribution  and  use  in  source  and  binary  forms,  with  or  without
 *  modification,  are  permitted  provided  that  the  following  conditions  are  met:
 *
 *  -Redistribution  of  source  code  must  retain  the  above  copyright  notice,  this
 *    list  of  conditions  and  the  following  disclaimer.
 *
 *  -Redistribution  in  binary  form  must  reproduce  the  above  copyright  notice,
 *    this  list  of  conditions  and  the  following  disclaimer  in  the  documentation
 *    and/or  other  materials  provided  with  the  distribution.
 *
 *  Neither  the  name  of  IFAA  or  the  names  of  contributors  may
 *  be  used  to  endorse  or  promote  products  derived  from  this  software  without
 *  specific  prior  written  permission.
 *
 *  This  software  is  provided  "AS  IS,"  without  a  warranty  of  any  kind.  ALL
 *  EXPRESS  OR  IMPLIED  CONDITIONS,  REPRESENTATIONS  AND  WARRANTIES,  INCLUDING
 *  ANY  IMPLIED  WARRANTY  OF  MERCHANTABILITY,  FITNESS  FOR  A  PARTICULAR  PURPOSE
 *  OR  NON-INFRINGEMENT,  ARE  HEREBY  EXCLUDED.  IFAA,  ORG.
 *  AND  ITS  LICENSORS  SHALL  NOT  BE  LIABLE  FOR  ANY  DAMAGES  SUFFERED  BY  LICENSEE
 *  AS  A  RESULT  OF  USING,  MODIFYING  OR  DISTRIBUTING  THIS  SOFTWARE  OR  ITS
 *  DERIVATIVES.  IN  NO  EVENT  WILL  IFAA  OR  ITS  LICENSORS  BE  LIABLE  FOR  ANY  LOST
 *  REVENUE,  PROFIT  OR  DATA,  OR  FOR  DIRECT,  INDIRECT,  SPECIAL,  CONSEQUENTIAL,
 *  INCIDENTAL  OR  PUNITIVE  DAMAGES,  HOWEVER  CAUSED  AND  REGARDLESS  OF  THE  THEORY
 *  OF  LIABILITY,  ARISING  OUT  OF  THE  USE  OF  OR  INABILITY  TO  USE  THIS  SOFTWARE,
 *  EVEN  IF  IFAA  HAS  BEEN  ADVISED  OF  THE  POSSIBILITY  OF  SUCH  DAMAGES.
 *
 *  You  acknowledge  that  this  software  is  not  designed,  licensed  or  intended
 *  for  use  in  the  design,  construction,  operation  or  maintenance  of  any
 *  nuclear  facility.
 *
 */
package com.ifaa.sdk.authenticatorservice.fingerprint.flow;

import android.content.Context;
import android.os.Bundle;

import com.ifaa.sdk.auth.AuthenticatorCallback;
import com.ifaa.sdk.authenticatorservice.fingerprint.constants.IFAAFingerprintConstants;
import com.ifaa.sdk.authenticatorservice.fingerprint.manager.IFAAFingerprintCallback;
import com.ifaa.sdk.authenticatorservice.fingerprint.manager.IFAAFingerprintManagerAdapter;
import com.ifaa.sdk.authenticatorservice.fingerprint.util.FingerprintDataUtil;
import com.ifaa.sdk.authenticatorservice.message.Result;
import com.ifaa.sdk.auth.AuthenticatorLOG;
import com.ifaa.sdk.auth.message.AuthenticatorMessage;
import com.ifaa.sdk.auth.message.AuthenticatorResponse;

import android.os.ConditionVariable;



/**
 * 
 * @author qiyi.wxc
 * @version 1.0
 */
public abstract class FingerprintAuth {

    protected Context mContext;
    protected Bundle mMessage;

    protected String mFacetId;

    protected String mAAID;

    protected int mTimeout = IFAAFingerprintConstants.FINGERPRINT_DEFAULT_TIMEOUT;
    protected final int MAXRETRYTIMES = 3;
    protected int retry = 0;

    protected boolean isFingerprintAuthSuccess = false;
    protected boolean isFingerprintAuthCancel = false;
    protected boolean isNotMatch = false;
    protected AuthenticatorCallback mCallback;

    public FingerprintAuth(Context context, Bundle message, AuthenticatorCallback callback) {
        mContext = context;
        mMessage = message;
        mCallback = callback;

        if (mMessage != null) {
            mTimeout = mMessage.getInt(AuthenticatorMessage.KEY_TIME_OUT,
                IFAAFingerprintConstants.FINGERPRINT_DEFAULT_TIMEOUT);
        }

        mFacetId = context.getPackageName();
    }

    private boolean isFinish = false;
    private boolean isErrorTimesLimit = false;
    private boolean isTimeout = false;

    private static FingerprintAuth curFingerprintAuth;
    private static ConditionVariable block  = new ConditionVariable();

    /**
     * 模板方法，所有校验流程统一的对外接口
     * 
     * @return
     */
    synchronized public final Bundle doAuth() {
        isFinish = false;
        isTimeout = false;
        isErrorTimesLimit = false;
        retry = 0;
        curFingerprintAuth = this;
        block.close();
        if (mMessage == null || mContext == null) {
            return FingerprintDataUtil.constructResultBundle(getReponseType(),
                AuthenticatorResponse.RESULT_FAILURE);
        }

        //控制cancel得到回调后才推出

        int operation = mMessage.getInt(AuthenticatorMessage.KEY_OPERATIONT_TYPE);


        if (AuthenticatorMessage.MSG_REGISTER_REQUEST == operation
            || AuthenticatorMessage.MSG_AUTHENTICATOR_REQUEST == operation) {
            // start fingerprint manager to auth
            isFingerprintAuthSuccess = false;
            isNotMatch = false;
            isFingerprintAuthCancel = false;
            final IFAAFingerprintManagerAdapter adapter = IFAAFingerprintManagerAdapter
                .getInstance(mContext);
            mAAID = adapter.getDeviceModel();
            adapter.authenticate(new IFAAFingerprintCallback() {

                @Override
                public void onAuthenticationSucceeded() {
                    // check SEND MESSAGE
                    isFingerprintAuthSuccess = true;
                    if (mCallback != null) {
                        mCallback.onStatus(Result.StatusCode.STATUS_INPUTTING);
                        mCallback.onStatus(Result.StatusCode.RESULT_SUCCESS);
                    }
                    block.open();

                }

                @Override
                public void onAuthenticationFailed() {
                    if (!isFinish) {
                        if (mCallback != null) {
                            mCallback.onStatus(Result.StatusCode.STATUS_INPUTTING);
                            mCallback.onStatus(Result.StatusCode.RESULT_NO_MATCH);
                        }
                    }
                    isFingerprintAuthSuccess = false;
                    retry++;
                    // check SEND MESSAGE
                    if (retry >= 3) {
                        isNotMatch = true;
                        block.open();
                    }
                }

                @Override
                public void onAuthenticationError(int errorCode, CharSequence errString) {
                    // check SEND MESSAGE
                    AuthenticatorLOG.fpInfo("onAuthenticationError errorcode:" + errorCode);
                    if (!isFinish) {
                        switch (errorCode){
                            case 3:         //FingerprintManager.FINGERPRINT_ERROR_TIMEOUT
                                isTimeout = true;
                                break;
                            case 5:
                                isFingerprintAuthCancel = true;
                                AuthenticatorLOG.fpInfo("cancel onResult");
                                break;
                            case 7:         //FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
                                isErrorTimesLimit = true;
                                break;
                            default:
                                break;
                        }
                        if (mCallback != null) {
                            mCallback.onStatus(Result.StatusCode.STATUS_INPUTTING);
                        }
                        if(errorCode != 5) {
                            if (mCallback != null) {
                                mCallback.onStatus(Result.StatusCode.RESULT_FAIL);
                            }
                        }else{      //FingerprintManager.FINGERPRINT_ERROR_CANCELED
                            if (mCallback != null) {
                                mCallback.onStatus(Result.StatusCode.RESULT_CANCELED);
                            }
                        }
                    }
                    isFingerprintAuthSuccess = false;
                    block.open();
                }
            });

            if (mCallback != null) {
                mCallback.onStatus(Result.StatusCode.STATUS_WAITING_FOR_INPUT);
            }
            int timeout = 120 * 1000;

            try {
                boolean open = block.block(timeout);
                isFinish = true;
                if (curFingerprintAuth != this) {
                    // 当前的auth流程非最新的，说明之前有流程没有cancel掉
                    adapter.cancel();
                    return null;
                }
                if (!open) {
                    isTimeout = true;
                }
                if (isNotMatch || isTimeout) {
                    adapter.cancel();
                }
                if (isTimeout) {
                    if (mCallback != null) {
                        mCallback.onStatus(Result.StatusCode.STATUS_INPUTTING);
                        mCallback.onStatus(Result.StatusCode.RESULT_TIMEOUT);
                    }
                }
            } catch (Exception e) {
                AuthenticatorLOG.error(e);
            }

            if (isNotMatch) {
                return FingerprintDataUtil.constructResultBundle(getReponseType(),
                    AuthenticatorResponse.RESULT_NO_MATCH);
            }
            if (isFingerprintAuthCancel) {
                return FingerprintDataUtil.constructResultBundle(getReponseType(),
                        AuthenticatorResponse.RESULT_CANCELED);
            }
            if (isTimeout) {
                return FingerprintDataUtil.constructResultBundle(getReponseType(),
                    AuthenticatorResponse.RESULT_TIMEOUT);
            }

            if (isErrorTimesLimit) {
                return FingerprintDataUtil.constructResultBundle(getReponseType(),
                    AuthenticatorResponse.RESULT_SYSTEMBLOCK);
            }
        }
        return doTransaction();
    }

    /**
     * 每种校验方式的事务逻辑
     */
    protected abstract Bundle doTransaction();

    /**
     * 每种校验方式的返回结果类型
     */
    protected abstract int getReponseType();

}
